#include <android/log.h>
#include "record.h"
#include "android/bitmap.h"
#include "global.h"

#define TAG "record"

Record::Record() {
	_pkgPtsIndex = 0;
	scopeOn = false;
}

Record::~Record() {
}

bool Record::start(const char* outputFile, int videoWidth, int videoHeight,
		int fps) {
	__android_log_print(ANDROID_LOG_ERROR, TAG, "record  start");
	if (scopeOn) {
		__android_log_print(ANDROID_LOG_ERROR, TAG, "record  start return");
		return false;
	}
	__android_log_print(ANDROID_LOG_ERROR, TAG, "record  start1");
	recordFps = fps;
	av_register_all();
	avformat_alloc_output_context2(&oc, NULL, NULL, outputFile);
	if (!oc) {
		printf(
				"Could not deduce output format from file extension: using MPEG.\n");
		avformat_alloc_output_context2(&oc, NULL, "mpeg", outputFile);
	}

	// 强制指定 264 编码
//	oc->oformat->video_codec = CODEC_ID_H264;

	fmt = oc->oformat;

	/* Add the audio and video streams using the default format codecs
	 * and initialize the codecs. */
	video_st = NULL;

	//fmt->video_codec = AV_CODEC_ID_H264;
	if (fmt->video_codec != AV_CODEC_ID_NONE) {
		picWidth = videoWidth;
		picHeight = videoHeight;

		video_st = add_stream(oc, &video_codec, fmt->video_codec,
		STREAM_PIX_FMT);
	}

	mVideoSwsContext = NULL;
	mVideoSwsContext = sws_getContext(picWidth, picHeight, PIX_FMT_RGBA,
			picWidth, picHeight,
			STREAM_PIX_FMT, SWS_BICUBIC, NULL, NULL, NULL);

	/* Now that all the parameters are set, we can open the audio and
	 * video codecs and allocate the necessary encode buffers. */
	if (video_st) {
		if (!open_video(oc, video_codec, video_st)) {
			return false;
		}
	}

	av_dump_format(oc, 0, outputFile, 1);
	int ret;

	/* open the output file, if needed */
	if (!(fmt->flags & AVFMT_NOFILE)) {
		ret = avio_open(&oc->pb, outputFile, AVIO_FLAG_WRITE);
		if (ret < 0) {
			fprintf(stderr, "Could not open '%s': %s\n", outputFile,
					av_err2str(ret));
			return false;
		}
	}

	/* Write the stream header, if any. */
	ret = avformat_write_header(oc, NULL);
	if (ret < 0) {
		fprintf(stderr, "Error occurred when opening output file: %s\n",
				av_err2str(ret));
		return false;
	}

	videoPtsIndex = 0;
	frame_count = 0;
	scopeOn = true;
	requireScopeStop = false;
	__android_log_print(ANDROID_LOG_ERROR, TAG, "record  end");

	return true;
}

bool Record::stop() {

	if (av_write_trailer(oc) != 0) {
		return false;
	}
	/* Close each codec. */
	if (video_st) {
		close_video(oc, video_st);
	}

	if (!(fmt->flags & AVFMT_NOFILE)) {
		if (avio_close(oc->pb) < 0) {
			return false;
		}
	}
	/* free the stream */
	avformat_free_context(oc);

}

bool Record::open_video(AVFormatContext *oc, AVCodec *codec, AVStream *st) {
	int ret;
	AVCodecContext *c = st->codec;

	//AVCodecContext *c = avcodec_alloc_context3(codec);
	/* open the codec */
	ret = avcodec_open2(c, codec, NULL);
	if (ret < 0) {
		fprintf(stderr, "Could not open video codec: %s\n", av_err2str(ret));
		return false;
	}

	if (mVideoSwsContext != NULL) {
		mFrame = av_frame_alloc();
		if (!mFrame) {
			return false;
		}

		ret = avpicture_alloc(&mPicture, STREAM_PIX_FMT, c->width, c->height);
		if (ret < 0) {
			return false;
		}

		//copy data and linesize picture pointers to frame
		*((AVPicture *) mFrame) = mPicture;
	}

	return true;
}

bool Record::write_video_frame(AVStream *st, AVFrame *frame) {
	int ret;
	AVCodecContext *c = st->codec;

	AVPacket pkt = { 0 };
	av_init_packet(&pkt);
	if (fmt->flags & AVFMT_RAWPICTURE) {
		pkt.flags |= AV_PKT_FLAG_KEY;
		pkt.stream_index = st->index;
		pkt.data = mPicture.data[0];
		pkt.size = sizeof(AVPicture);

		ret = av_interleaved_write_frame(oc, &pkt);
	} else {
		int got_packet = 0;

		/* encode the image */
		if (mVideoSwsContext != NULL) {
			sws_scale(mVideoSwsContext, frame->data, frame->linesize, 0,
					picHeight, mPicture.data, mPicture.linesize);

			//important, to control fps of the mp4
			mFrame->pts = frame->pts;
			ret = avcodec_encode_video2(c, &pkt, mFrame, &got_packet);
		} else {
			ret = avcodec_encode_video2(c, &pkt, frame, &got_packet);
		}

		if (ret < 0) {
			fprintf(stderr, "Error encoding video frame: %s\n",
					av_err2str(ret));
			return false;
		}
		/* If size is zero, it means the image was buffered. */

		if (!ret && got_packet && pkt.size > 0) {
			pkt.stream_index = st->index;

			/* Write the compressed frame to the media file. */
			ret = av_interleaved_write_frame(oc, &pkt);
		} else {
			ret = 0;
		}
	}

	if (ret != 0) {
		fprintf(stderr, "Error while writing video frame: %s\n",
				av_err2str(ret));
		return false;
	}

	frame_count++;
	return true;
}

void Record::record(unsigned char* rgbData, int pts) {
	__android_log_print(ANDROID_LOG_ERROR, TAG, "record begin");
	int ret;
	AVCodecContext *c = video_st->codec;
	AVPacket pkt = { 0 };
	int got_packet = 0;
	av_init_packet(&pkt);
	m_pRGBFrame = av_frame_alloc();
	m_pYUVFrame = av_frame_alloc();
	uint8_t * yuv_buff;
	int size = picWidth * picHeight;
	yuv_buff = (uint8_t *) malloc((size * 3) / 2);
	__android_log_print(ANDROID_LOG_ERROR, TAG, "record begin 1");
	avpicture_fill((AVPicture*) m_pRGBFrame, (uint8_t*) rgbData, PIX_FMT_ARGB,
			picWidth, picHeight);
	__android_log_print(ANDROID_LOG_ERROR, TAG, "record begin 1.5");
	//将YUV buffer 填充YUV Frame
	avpicture_fill((AVPicture*) m_pYUVFrame, (uint8_t*) yuv_buff,
			PIX_FMT_YUV420P, picWidth, picHeight);
	__android_log_print(ANDROID_LOG_ERROR, TAG, "record begin 2");

	//将RGB转化为YUV
	sws_scale(mVideoSwsContext, m_pRGBFrame->data, m_pRGBFrame->linesize, 0,
			picHeight, m_pYUVFrame->data, m_pYUVFrame->linesize);
	__android_log_print(ANDROID_LOG_ERROR, TAG, "record begin 3");
	ret = avcodec_encode_video2(c, &pkt, m_pYUVFrame, &got_packet);
	__android_log_print(ANDROID_LOG_ERROR, TAG, "record begin  4+pts=%d",
			videoPtsIndex);
	_pkgPtsIndex += 1000;

	pkt.pts = videoPtsIndex;
	pkt.dts = pkt.pts;
	videoPtsIndex += av_rescale_q(1, video_st->codec->time_base,
			video_st->time_base);

	//Convert PTS/DTS
//	pkt.pts = av_rescale_q_rnd(pts - _pkgPtsIndex,
//			video_st->time_base, video_st->time_base,
//			(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
//	pkt.dts = av_rescale_q_rnd(pts - _pkgDtsIndex,
//			video_st->time_base, video_st->time_base,
//			(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
//	pkt.duration = av_rescale_q(videoDuration, video_st->time_base,
//			video_st->time_base);
//	pkt.pos = -1;

	if (!ret && got_packet && pkt.size > 0) {
		pkt.stream_index = video_st->index;
		/* Write the compressed frame to the media file. */
		ret = av_interleaved_write_frame(oc, &pkt);
		__android_log_print(ANDROID_LOG_ERROR, TAG, "record write frame");
	} else {
		ret = 0;
	}
	frame_count++;
	av_frame_free(&m_pRGBFrame);
	av_frame_free(&m_pYUVFrame);
//	delete []rgbData;
	av_free_packet(&pkt);
	free(yuv_buff);
	__android_log_print(ANDROID_LOG_ERROR, TAG, "record begin end");
}

void Record::close_video(AVFormatContext *oc, AVStream *st) {
	avcodec_close(st->codec);
	frame_count = 0;
	if (mVideoSwsContext != NULL) {
		avpicture_free(&mPicture);
		av_frame_free(&mFrame);
	}
}

void Record::initialSampleData(AVCodecContext* c) {
	/* init signal generator */
	t = 0;
	tincr = 2 * M_PI * 110.0 / c->sample_rate;
	/* increment frequency by 110 Hz per second */
	tincr2 = 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate;
}

AVStream* Record::add_stream(AVFormatContext *oc, AVCodec **codec,
		AVCodecID codec_id, AVPixelFormat pixelFormat) {
	AVCodecContext *c;
	AVStream *st;

	/* find the encoder */
	*codec = avcodec_find_encoder(CODEC_ID_MPEG4);
	if (!(*codec)) {
		fprintf(stderr, "Could not find encoder for '%s'\n",
				avcodec_get_name(codec_id));
		__android_log_print(ANDROID_LOG_ERROR, TAG, "find h264 decoder fail");
//		exit(1);
	}

	st = avformat_new_stream(oc, *codec);
	if (!st) {
		fprintf(stderr, "Could not allocate stream\n");
		__android_log_print(ANDROID_LOG_ERROR, TAG, "find stream fail");
//		exit(1);
	}

	st->id = oc->nb_streams - 1;
	c = st->codec;

	switch ((*codec)->type) {
	case AVMEDIA_TYPE_AUDIO:
		//c->codec_id    = AV_CODEC_ID_PCM_MULAW;
		c->sample_fmt = AV_SAMPLE_FMT_S16; //AV_SAMPLE_FMT_FLTP; //
		c->bit_rate = 64000;
		c->sample_rate = 8000;   //44100
		c->channels = 1;   //2
		break;

	case AVMEDIA_TYPE_VIDEO:
		c->codec_id = codec_id;
		c->bit_rate = 1 * 1024 * 1024;
		/* Resolution must be a multiple of two. */
		c->width = picWidth;
		c->height = picHeight;
		/* timebase: This is the fundamental unit of time (in seconds) in terms
		 * of which frame timestamps are represented. For fixed-fps content,
		 * timebase should be 1/framerate and timestamp increments should be
		 * identical to 1. */
		c->time_base.den = recordFps;
		c->time_base.num = 1;
		c->gop_size = 10; /* emit one intra frame every twelve frames at most */
		if (pixelFormat == AV_PIX_FMT_NONE) {
			c->pix_fmt = STREAM_PIX_FMT;
		} else {
			c->pix_fmt = pixelFormat;
		}

		break;

	default:
		break;
	}

	/* Some formats want stream headers to be separate. */
	if (oc->oformat->flags & AVFMT_GLOBALHEADER)
		c->flags |= CODEC_FLAG_GLOBAL_HEADER;

	return st;
}
